# -*- coding: utf-8 -*-

"""
Date: 2023/2/27 14:06
Author: Darker
"""
import os
import time
from typing import Dict, Text, NoReturn

import PySimpleGUI as sg
import math
from PIL import ImageGrab, Image

from config import conf
from log import logger
from utils.cache_utils import cacheUtils
from utils.data_cleanup_utils import data_cleanup
from utils.date_utils import today
from utils.tapd_utils import tapd
from utils import g, search_result, img_process_utils, date_utils, email_utils


class UI(object):

    def __init__(self):
        self.__topLayout = None
        self.__emailLayout = None
        self.__resultLayout = None
        self.layout = []
        self.window = None

    @property
    def ui_keys(self):
        return ["cookie", "id", "start_time", "smtp_server", "email_user", "email_passwd", "iteration_id",
                "title", "os1", "testPhase", "addressee", "mail_cc"]

    @property
    def monthNames(self):
        monthDict = {
            "1": "一",
            "2": "二",
            "3": "三",
            "4": "四",
            "5": "五",
            "6": "六",
            "7": "七",
            "8": "八",
            "9": "九",
            "10": "十",
            "11": "十一",
            "12": "十二"
        }
        return [f"{monthDict.get(str(month))}月" for month in range(1, 13)]

    def init_window(self, data: Dict):
        self.__init_layout(data=data)
        self.window = sg.Window(title="tapd_bug_reports", layout=self.layout,
                                finalize=True, enable_close_attempted_event=True)

    def __top_layout_area(self, data: Dict):
        """顶部条件输入区域"""
        # 第一行
        firstRow = [
            sg.Button(key="url_method", button_text="* TAPD_Cookie：", size=15,
                      button_color="#64778D", border_width=0, tooltip="点击可切换数据请求方式"),
            sg.Input(key="cookie", size=33, default_text=data.get("cookie")),
            sg.Button(button_text="烎", tooltip="更新项目", size=3, mouseover_colors="#7c8577", use_ttk_buttons=True),
            sg.Text(text="* 项目：", size=9, justification="right"),
            sg.InputCombo(key="id", size=31, default_value=data.get("id"),
                          values=data.get("listId").split("_toolFlag_"), enable_events=True),
            sg.Text("* 测试起止时间：", size=15, justification="right"),
            sg.Input(key="start_time", size=10, default_text=data.get("startTime", data.get("start_time")),
                     tooltip="1970-01-01"),
            sg.Text("-", size=1),
            sg.Input(key="end_time", size=10, default_text=today(), tooltip="9999-12-31"),
            sg.CalendarButton(button_text="DATE", font=("MS Sans Serif", 1, "bold"),
                              button_color=("#fffffb", "#11264f"), format="%Y-%m-%d", key="_CALENDAR_",
                              target="start_time", tooltip="选择测试开始时间", month_names=self.monthNames,
                              enable_events=True)
        ]
        # 第二行
        secondRow = [
            sg.Text(text="迭代版本：", size=15, justification="right"),
            sg.InputCombo(key="iteration_id", size=31, default_value=data.get("iteration_id"),
                          tooltip="根据迭代版本筛选缺陷", values=data.get("listIteration").split("_tooFlag_"),
                          enable_events=True),
            sg.Text(text="发布计划：", size=15, justification="right"),
            sg.InputCombo(key="release_id", size=31, default_value=data.get("releaseId"),
                          values=data.get("listReleaseId").split("_tooFlag_"), tooltip="项目发布计划id",
                          enable_events=True),
            sg.Text(text="操作系统：", size=15, justification="right"),
            sg.InputCombo(key="os1", size=31, default_value=data.get("os1"),
                          values=data.get("listOs").split("_toolFlag_"), enable_events=True,
                          tooltip="根据缺陷所选操作系统筛选，支持手动输入")
        ]
        # 第三行
        threeRow = [
            sg.Text(text="测试阶段：", size=15, justification="right"),
            sg.InputCombo(key="testPhase", size=31, default_value=data.get("testPhase"),
                          tooltip="根据缺陷所选测试阶段筛选缺陷，支持手动输入",
                          values=data.get("listTestPhase").split("_tooFlag_"), enable_events=True),
            sg.Text(text="是否包含节假日：", size=15, justification="right"),
            sg.Radio(text="是", group_id="TADIO1", key="b2", default=data.get("b2") is True),
            sg.Radio(text="否", group_id="TADIO1", key="b1", default=data.get("b1") is True, size=20),
            sg.Text(text="获取数据URL：", size=15, justification="right", visible=False),  # 留一个隐藏占位
            sg.Input(key="url_tapd", size=141, default_text=data.get("tapdUrl"), visible=False),
            sg.Checkbox(text="邮件定时/min：", key="timer", default=False),
            sg.Slider(key="timer_slider", range=(1, 1440), default_value=20, size=(10, 10), expand_x=True,
                      orientation="horizontal", font=("Helvetica", 10))
        ]
        # 第四行
        fourRow = [
            sg.Text(text="* 邮件服务器地址：", key="text1", size=15, justification="right", visible=True),
            sg.Input(key="smtp_server", size=33, default_text=data.get("smtpServer"), tooltip="smtp.email.qq.com",
                     visible=True),
            sg.Text(text="* 邮件服务器账号：", key="text2", size=15, justification="right", visible=True),
            sg.Input(key="email_user", size=33, default_text=data.get("userEmail", data.get("email_user")), tooltip="xxxxxx@ztccloud.com.cn",
                     visible=True),
            sg.Text(text="* 邮件服务器密码：", key="text3", size=15, justification="right", visible=True),
            sg.Input(key="email_passwd", size=33, default_text=data.get("emailPassword", data.get("email_passwd")),
                     visible=True),
        ]
        # 第五行
        fiveRow = [
            sg.Text(text="邮件主题：", size=15, justification="right"),
            sg.Input(key="mail_title", size=33, default_text=data.get("emailTitle")),
            sg.Text(key="text4", text="邮件收件人：", size=15, justification="right", visible=True),
            sg.Input(key="address", size=33, tooltip="多个收件人用英文逗号隔开", default_text=data.get("address"),
                     visible=True),
            sg.Text(key="text5", text="邮件抄送人：", size=15, justification="right", visible=True),
            sg.Input(key="mail_cc", size=33, tooltip="多个抄送人用英文逗号隔开", default_text=data.get("mailCC"),
                     visible=True)
        ]
        # 按钮行
        buttonRow = [
            sg.Button(button_text="源码", size=10, mouseover_colors="#1d953f", use_ttk_buttons=True, right_click_menu=[
                "&Right", ["你的队友：", "【镇山的虎】", "【远见的鹰】", "【忠诚的狗】", "【善战的狼】",
                           "而你：", "【害群的马】", "【盛饭的桶】", "【墙头的草】", "【搅屎的棍】", ["【卸载工具】"]]]),
            sg.Text(text="  ", size=16, justification="left"),  # 空白区域
            sg.Text(text="  ", size=1, justification="left"),  # 空白区域
            sg.Text(text="  ", size=31, justification="left"),  # 空白区域
            sg.Button(button_text="执行", bind_return_key=True, size=10, mouseover_colors="#1d953f",
                      use_ttk_buttons=True),
            sg.Button(button_text="保存", size=10, mouseover_colors="#dec674", use_ttk_buttons=True,
                      tooltip="保存界面输入的内容"),
            sg.Button(button_text="清空", size=10, mouseover_colors="#dec674", use_ttk_buttons=True,
                      tooltip="清空执行日志"),
        ]
        self.__topLayout = [firstRow, secondRow, threeRow, fourRow, fiveRow, buttonRow]

    def __email_content_area(self, data: Dict):
        """邮件正文组成部分区域"""
        emailArea = [
            [sg.Text(text="邮件开篇：", size=15, justification="left")],
            [sg.Multiline(key="daily_text", size=(75, 6), default_text=data.get("dailyText"))],
            [
                sg.Text(text="里程碑总体进度：", size=13, justification="left"),
                sg.Input(key="milestone_address", size=47, default_text=data.get("milestone_address"),
                         tooltip="里程碑在线文档地址，用于展示"),
                sg.Button(button_text="贴图", size=4, mouseover_colors="#dec674", use_ttk_buttons=True,
                          tooltip="将剪切板的截图贴到邮件里程碑位置"),
                sg.Button(button_text="预览", size=4, mouseover_colors="#dec674", use_ttk_buttons=True,
                          tooltip="查看里程碑截图")
            ],
            [
                sg.Multiline(key="milestone_excel", size=(75, 4),
                             tooltip="可粘贴里程碑在线文档内容，请直接粘贴excel\ntip:\n单元格合并格式只会保留第一列\n"
                                     "单元格里边存在换行也会有异常...")
            ],
            [
                sg.Text(text="今日测试执行情况：", size=15, justification="left"),
                sg.Input(key="manpower_today", size=3, default_text=data.get("manpower_today"), justification="right",
                         background_color="#009ad6")
            ],
            [sg.Multiline(key="test_situation", size=(75, 4), default_text=data.get("testSituation"))],
            [sg.Text(text="明日计划：", auto_size_text=True, justification="left")],
            [sg.Multiline(key="tomorrow_plans", size=(75, 4), default_text=data.get("tomorrowPlans"))]
        ]
        self.__emailLayout = emailArea

    def __result_content_area(self):
        """执行结果区域"""
        self.__resultLayout = [
            [
                sg.Output(key="debug_result", size=(75, 26), echo_stdout_stderr=True)
            ]
        ]

    def __overall_layout(self):
        """整体布局"""
        self.layout = [
            [
                sg.Frame(key="frame1", title="", layout=self.__topLayout)
            ],
            [
                sg.Frame(title=" 邮件内容", layout=self.__emailLayout),
                sg.Frame(title=" 执行日志", layout=self.__resultLayout)
            ]
        ]

    def __init_layout(self, data: Dict):
        self.__top_layout_area(data=data)
        self.__email_content_area(data=data)
        self.__result_content_area()
        self.__overall_layout()

    def event_handler(self) -> NoReturn:
        configData = g.configData

        event, value = ui.window.read(300)
        # 界面填写的值去除左右的空格
        for key, val in value.items():
            if not isinstance(key, str):
                break
            if key in ui.ui_keys:
                value[key] = val.strip()
        cookie = value.get("cookie", "").replace("_toolFlag_", "%")  # 处理cookie字段
        if cookie:
            cacheUtils.write_cache({"cookies": cookie})
        match event:
            case "清空":
                self.window["debug_result"].Update(value="")
            case "collapse_control":
                # 界面折叠功能
                if self.window[event].get_text() == "︽":
                    visibleFlag = False
                else:
                    visibleFlag = True
                self.window[event].Update(text="︾")
                self.window["frame1"].update(visible=visibleFlag)
            case "url_method":
                if g.win2_active or configData["win2_flag"] == "Y":
                    pass
            case "暂存":
                logger.info("暂存成功")
            case "贴图":
                im = ImageGrab.grabclipboard()
                im.save(conf.milestonePath)
            case "预览":
                if not os.path.exists(conf.milestonePath):
                    msg = "【里程碑图片】不存在，请先贴图"
                    logger.error(msg)
                    sg.popup(msg)
                else:
                    # 预览里程碑截图
                    with Image.open(conf.milestonePath) as imgReader:
                        imgReader.show()
            case "GET" | "烎":
                if not cookie:
                    sg.popup("【TAPD_Cookie】不能为空")
                else:
                    idName = tapd.get_project_ids()
                    if idName:
                        g.idName = list(f"{i[0]}--{i[1]}" for i in zip(idName.values(), idName.keys()))
                        self.window["id"].Update(values=g.idName)
                        conf.modify_config("TAPD", "listId", "_toolFlag_".join(idName))
                    else:
                        sg.popup("获取项目列表为空，可手动输入【项目id】")
            case "id":
                # 界面出发id列表选择时间，like回复下拉列表的内容完整
                # 刷新获取迭代版本
                self.window["id"].Update(value=value.get("id"), values=g.idName)
                if not cookie:
                    sg.popup("【TAPD_Cookie】不能为空")
                else:
                    if g.lastSelectProjectId == value.get("id"):
                        return
                    g.lastSelectProjectId = value.get("id")
                    # 获取迭代版本id
                    logger.info("正在获取迭代版本id，请稍等")
                    iterationUpdate = tapd.get_iteration_versions(value.get("id", "")[-8:])
                    if iterationUpdate:
                        g.iterationUpdate = list(f"{i[0]}--{i[1]}" for i in zip(iterationUpdate.values(),
                                                                                iterationUpdate.keys()))
                        self.window["iteration_id"].Update(values=g.iterationUpdate)
                        logger.info("成功获取所选项目下的【迭代版本】")
                    else:
                        logger.error(
                            "获取当前项目【迭代版本】失败，请检查【TAPD_Cookie】、【项目】字段。或手动填写迭代版本id")
                        return
            case "iteration_id":
                if g.lastSelectIterationId == value.get("iteration_id"):
                    return
                g.lastSelectIterationId = value.get("iteration_id")
                # 界面出发迭代版本id选择事件，还原迭代版本下拉列表内容
                self.window["iteration_id"].Update(value=value.get("iteration_id"),
                                                   values=g.iterationUpdate)
                g.iterationId = value.get("iteration_id")
                # 获取发布计划id
                logger.info("正在获取发布计划id，请稍等")
                releaseUpdate = tapd.get_release_ids(value.get("id")[-8:])
                if not releaseUpdate:
                    return
                g.releaseUpdate = list(f"{i[0]}--{i[1]}" for i in zip(releaseUpdate.values(),
                                                                      releaseUpdate.keys()))
                self.window["release_id"].update(values=g.releaseUpdate)
                logger.info("成功获取所选项目下的【发布计划】")
            case "release_id":
                self.window["release_id"].Update(value=value.get("release_id"),
                                                 values=g.releaseUpdate)
                g.oldReleaseId = value.get("release_id")
            case sg.WINDOW_CLOSE_ATTEMPTED_EVENT | "EXIT":
                return "break"
            case "执行":
                # 校验所有必填项，这一步先空着
                # TODO 本次先不处理，以后有时间再搞
                # 获取缺陷数据

                # 保存配置
                if not g.lastValue:
                    g.lastValue = value
                conf.merge_config(value)
                projectId = value.get("id").split("--")[-1]
                startTime = value.get("start_time")
                endTime = value.get("end_time")
                title = ""
                iterationId = value.get("iteration_id").split("--")[-1]
                os1 = value.get("os1")
                testPhase = value.get("testPhase")
                releaseId = value.get("release_id").split("--")[-1]
                viewId, confId = tapd.get_bug_fields_user_view_and_list(projectId)
                # statusOld = tapd.get_show_fields(projectId, viewId)  # 获取现有TAPD缺陷显示字段
                # if not statusOld:
                #     return
                # 设置TAPD缺陷显示字段为预设字段
                visibleFieldTmp = tapd.set_show_fields(projectId, viewId, tapd.statusTool)
                if not visibleFieldTmp:
                    return
                bugsNumb = tapd.get_bugs_list(projectId, confId, startTime, endTime, title, iterationId, os1,
                                              testPhase, releaseId, flag="total_count")  # 获取缺陷总数
                if not isinstance(bugsNumb, int):
                    return
                elif bugsNumb == 0:
                    buttonReturn = sg.popup_ok_cancel("项目下当前迭代版本缺陷总数为0，请先确认")
                    if buttonReturn == "Cancel":
                        logger.warn("当前迭代版本缺陷总数为0，终止执行")
                        return

                # 得到所有缺陷的字段数据，并记录成insert语句
                logger.info(f"共【{bugsNumb}】条缺陷，正在生成图片，请稍等")
                sqlList = []
                for i in range(1, math.ceil(bugsNumb / 100) + 1):
                    dataTapd = tapd.get_bugs_list(projectId, confId, startTime, endTime, title, iterationId, os1,
                                                  testPhase, releaseId, page=i)
                    if not dataTapd:
                        continue
                    sqlList.extend(data_cleanup(dataTapd))

                # 读取里程碑输入框内容，如果是excel粘贴的，则处理成html中的table
                # TODO 本次先不处理，以后有时间再搞

                """整理数据存放与全局对象g, 用于后续发邮件"""
                # TODO 里程碑基本不用，此处先不写
                tableText = value.get("milestone_excel", "")
                dateList = date_utils.calculate_date(value.get("start_time"),
                                                     value.get("end_time"),
                                                     True if value.get("b2") else False)

                g.emailUser = value.get("email_user")
                g.emailPassword = value.get("email_passwd")
                g.addressList = value.get("addressee", f"{value.get('email_user')}").replace("，", ",").split(",")
                g.mailCCList = value.get("mail_cc", "").replace("，", ",").split(",")
                g.smtpServer = value.get("smtp_server").strip()
                g.emailData = img_process_utils.analyze_datas(sqlList, dateList, projectId)
                g.listVals = [
                    value.get("daily_text"), value.get("test_situation"), value.get("tomorrow_plans"),
                    value.get("manpower_today"), value.get("milestone_address"), tableText, value.get("mail_title")
                ]
                if not all((g.emailUser,
                            g.emailPassword,
                            g.addressList,
                            g.mailCCList,
                            g.smtpServer,
                            g.emailData,
                            g.listVals)):
                    logger.warn("请检查所有必填项是否均已填写")
                    return
                else:
                    email_utils.send_email()  # 发送邮件
                    logger.info("-" * 20)
                    logger.info(f"执行结束：{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")
                    logger.info("-" * 20)
                    return "break"

        # 下拉框筛选数据
        # 根据项目id字段输入框的内容实时筛选项目下拉列表
        filterStr = value.get("id")
        self.window["id"].Update(value=value.get("id"),
                                 values=search_result(g.idName, filterStr))

        # 根据迭代id实时筛选迭代下拉列表
        filterStr = value.get("iteration_id")
        self.window["iteration_id"].Update(value=value.get("iteration_id"),
                                           values=search_result(g.iterationUpdate, filterStr))

        # 根据发布计划id字段输入框的内容实时筛选迭代版本下拉列表
        filterStr = value.get("release_id")
        self.window["release_id"].Update(value=value.get("release_id"),
                                         values=search_result(g.releaseUpdate, filterStr))


ui = UI()
